package com.xckj.impl;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.xckj.ApplicationContext;
import com.xckj.BeanPostProcessor;
import com.xckj.InitializingBean;
import com.xckj.annotation.Autowired;
import com.xckj.annotation.Bean;
import com.xckj.annotation.Component;
import com.xckj.annotation.ComponentScan;
import com.xckj.annotation.Configuration;
import com.xckj.annotation.Lazy;
import com.xckj.annotation.Scope;

/**
 * @version 1.0
 */
public class AnnotationConfigApplicationContext<T> implements ApplicationContext<T> {
	private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
	/**
	 * 配置地址
	 */
	public final Class<T> configClass;
	/**
	 * 保存所有BeanPostProcessor接口实现类的集合
	 */
	List<BeanPostProcessor> list = new ArrayList<>();
	
    /**
     * 单例对象池
     */
    private final Map<String, Object> singletonObjects = new HashMap<>();

    public AnnotationConfigApplicationContext(Class<T> configClass) throws ClassNotFoundException {
        this.configClass = configClass;
        // 扫描组件
        scan(configClass);
        
        // 把组件中非懒加载的单例bean保存到单例池
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            BeanDefinition beanDefinition = entry.getValue();
            if(isSingleton(beanDefinition.getScope()) && !beanDefinition.isLazy()) {
                Object bean = createBean(beanDefinition);
                String beanName = entry.getKey();
                singletonObjects.put(beanName, bean);
            }
        }
    }

    /**
     * 扫描组件
     * @param clazz 配置类的类对象
     * @throws ClassNotFoundException 类找不到
     */
    private void scan(Class<T> clazz) throws ClassNotFoundException {
        // 如果类上使用了@ComponentScan注解
        if (clazz.isAnnotationPresent(ComponentScan.class)) {
            ComponentScan componentScan = clazz.getAnnotation(ComponentScan.class);
            String value = componentScan.value();
 
            if (!"".equals(value)) {
                String path = value;
                path = path.replace(".", "/");
 
                URL resource = clazz.getClassLoader().getResource(path);
                File file = new File(resource.getFile());
 
                loopFor(file);
            }
        }
    }
    
    /**
     * 递归遍历指定文件/文件夹
     * @param file 文件/文件夹
     * @throws ClassNotFoundException 类找不到
     */
    private void loopFor(File file) throws ClassNotFoundException {
        if (file.isDirectory()) {
            for (File listFile : file.listFiles()) {
                if (listFile.isDirectory()) {
                    loopFor(listFile);
 
                    continue;
                }
 
                toBeanDefinitionMap(listFile);
            }
        } else if (file.isFile()) {
            toBeanDefinitionMap(file);
        }
    }

    /**
     * 解析bean，并保存到Map<String, BeanDefinition>
     * @param file 解析的class文件
     * @throws ClassNotFoundException 类找不到
     */
    private void toBeanDefinitionMap(File file) throws ClassNotFoundException {
        // 获取类的绝对路径
        String absolutePath = file.getAbsolutePath();
        // 处理得到类的全限定名
        absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
        absolutePath = absolutePath.replace("\\", ".");
 
        // 通过类加载器加载
        Class<?> loadClass = configClass.getClassLoader().loadClass(absolutePath);
 
        if (loadClass.isAnnotationPresent(Component.class)) {
            Component component = loadClass.getAnnotation(Component.class);
            String beanName = component.value();
 
            if ("".equals(beanName)) {
                beanName = getBeanName(loadClass);
            }
 
            boolean lazy = false;
            String scope = "singleton";
 
            // 类上使用了@Scope注解
            if (loadClass.isAnnotationPresent(Scope.class)) {
                // 获取@Scope注解
                Scope annotation = loadClass.getAnnotation(Scope.class);
 
                // 单例
                if (isSingleton(annotation.value())) {
                    lazy = loadClass.isAnnotationPresent(Lazy.class);
                } else {
                    // 非单例
                    scope = annotation.value();
                }
            } else {
                // 类上没有使用@Scope注解，默认是单例的
                lazy = loadClass.isAnnotationPresent(Lazy.class);
            }
 
            // 保存bean的定义
            BeanDefinition beanDefinition = new BeanDefinition();
 
            beanDefinition.setType(loadClass);
            beanDefinition.setLazy(lazy);
            beanDefinition.setScope(scope);
 
            // BeanPostProcessor接口的实现类保存到list
            if (BeanPostProcessor.class.isAssignableFrom(loadClass)) {
                list.add((BeanPostProcessor) createBean(beanDefinition));
            } else {
                beanDefinitionMap.put(beanName, beanDefinition);
            }
            beanDefinitionMap.put(beanName, beanDefinition);
        } else if (loadClass.isAnnotationPresent(Configuration.class)) {
            Method[] methods = loadClass.getDeclaredMethods();
 
            for (Method method : methods) {
                if (method.isAnnotationPresent(Bean.class)) {
                    Bean annotation = method.getAnnotation(Bean.class);
                    String beanName = annotation.value();
 
                    // 是否懒加载
                    boolean lazy = method.isAnnotationPresent(Lazy.class);
                    // 作用域
                    String scope = "singleton";
 
                    if ("".equals(beanName)) {
                        beanName = method.getName();
                    }
 
                    // 保存bean的定义
                    BeanDefinition beanDefinition = new BeanDefinition();
 
                    // 方法的返回值类型就是bean的类型
                    beanDefinition.setType(method.getReturnType()); // 方法的返回值类型就是bean的类型
                    beanDefinition.setLazy(lazy);
                    beanDefinition.setScope(scope);
 
                    // BeanPostProcessor接口的实现类保存到list
                    if (BeanPostProcessor.class.isAssignableFrom(loadClass)) {
                        list.add((BeanPostProcessor) createBean(beanDefinition));
                    } else {
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }
                }
            }
        }
    }
 
    /**
     * 根据类对象获取beanName
     * @param clazz bean的Class对象
     * @return String bean名称
     */
    private String getBeanName(Class<?> clazz) {
        String beanName = clazz.getSimpleName();
 
        // 判断是否以双大写字母开头
        String className = beanName.replaceAll("([A-Z])([A-Z])", "$1_$2");
 
        // 正常的大驼峰命名：bean名称为类名首字母大写
        if (className.indexOf("_") != 1) {
            beanName = beanName.substring(0, 1).toLowerCase().concat(beanName.substring(1));
        } else { // 否则，bean名称为类名
            beanName = beanName;
        }
 
        return beanName;
    }

    /**
     * 判断作用域是否单例
     * @param scope bean的作用域
     * @return boolean 如果是单例，返回true，否则返回false
     */
    private boolean isSingleton(String scope) {
        return "singleton".equals(scope);
    }
 
    /**
     * 创建bean对象
     * @param beanDefinition bean的定义
     * @return Object 创建好的bean对象
     */
    private Object createBean(BeanDefinition beanDefinition) {
        Object bean = null;
        Class beanType = beanDefinition.getType();
 
        // 获取所有构造方法
        Constructor[] constructors = beanType.getConstructors();
 
        try {
            /*
             * 推断构造方法
             * 1、没有提供构造方法：调用默认的无参构造
             * 2、提供了构造方法：
             *   - 构造方法个数为1
             *     - 构造方法参数个数为0：无参构造
             *     - 构造方法参数个数不为0：传入多个为空的参数
             *   - 构造方法个数 > 1：
             *     - 提供了无参构造方法：调用无参构造方法实例化
             *     - 没有提供无参构造方法：推断失败，抛出异常
             */
            if (isEmpty(constructors)) { // 注意：这个分支永远不会执行，可以删除，但是为了方便理解代码，在此保留
                // 无参构造方法
                Constructor constructor = beanType.getConstructor();
 
                bean = constructor.newInstance();
            } else if (constructors.length == 1) {
                Constructor constructor = constructors[0];
                // 得到构造方法参数个数
                int parameterCount = constructor.getParameterCount();
 
                if (parameterCount == 0) {
                    // 无参构造方法
                    bean = constructor.newInstance();
                } else {
                    // 多个参数的构造方法
                    Object[] array = new Object[parameterCount];
 
                    bean = constructor.newInstance(array);
                }
            } else {
                boolean success = false;
 
                for (Constructor constructor : constructors) {
                    if (constructor.getParameterCount() == 0) {
                        bean = constructor.newInstance();
                        success = true;
                        break;
                    }
                }
 
                if (!success) {
                    throw new IllegalStateException("No default constructor found.");
                }
            }
            
            /*
             * 处理字段注入
             * 先AutowiredByName后AutowiredByType
             */
            // 获取bean的所有自定义属性
            Field[] fields = beanType.getDeclaredFields();
 
            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    // 获取bean
                    // 通过bean名称获取bean
                    Object autowiredBean = getBean(field.getName());
 
                    // 没有找到对应名称的bean，尝试通过bean类型查找，bean的类型就是该字段的类型
                    if (autowiredBean == null) {
                        // 获取字段类型
                        Class<?> type = field.getType();
 
                        autowiredBean = getBean((Class<T>) type);
                    }
 
                    // 并设置到@Autowired注入的属性中
                    field.setAccessible(true);
                    field.set(bean, autowiredBean);
                }
            }
 
            /*
             * 初始化前
             */
            String beanName = getBeanName(beanDefinition.getType());
 
            if (!list.isEmpty()) {
                for (BeanPostProcessor beanPostProcessor : list) {
                    bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
                }
            }
 
            // 调用InitializingBean的afterPropertiesSet()方法
            if (bean instanceof InitializingBean) {
                ((InitializingBean) bean).afterPropertiesSet();
            }
 
            /*
             * 初始化后
             */
            if (list.size() > 0) {
                for (BeanPostProcessor beanPostProcessor : list) {
                    bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
                }
            }
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
 
        return bean;
    } 
    
    private boolean isEmpty(Object[] array) {
        return array.length == 0;
    }
	
    @Override
    public Object getBean(String beanName) {
        return null;
    }
 
    @Override
    public T getBean(Class<T> type) {
        return null;
    }
 
    @Override
    public T getBean(String beanName, Class<T> type) {
        return null;
    }
 
}
